/*
 * Copyright 2012 Alain Pannetrat (L1L1@gmx.com)
 * 
 * This file is part of cookie-miner.
 * 
 * cookie-miner is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * cookie-miner is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with cookie-miner. If not, see http://www.gnu.org/licenses/.
 *
 * @version: $Id$
 *
 */
#ifndef _GRAPHS_H_
#define _GRAPHS_H_

#include "vectors.h"
#include <cstdlib>
#include <set>
#include <list>
#include "dictionary.h"
#include "common.h"

class Universe;

class Vertex;

typedef std::list<Vertex*> VertexList;

class Graph;

typedef std::set<Graph*> GraphSet;

class Edge;

typedef std::list<Edge*> EdgeList;

class Vertex : private Uncopyable {
	public:
		friend class Universe;
		friend class Edge;
		friend class Graph;

		static Vertex *create(Universe& u, const Vector& the_pos, const std::string& name); 
		~Vertex();

		// Position

		const Vector& position() const { return _position; }
		void position(const Vector& pos);

		const Vector& force() const { return _force; }
		void force(const Vector& force) { _force = force; }

		float animate(float timestep);
		
		// characterictics

		const std::string& name() const { return _name; }
		
		const Vector& color() const { return _color; }
	        void color(const Vector& scolor) { _color = scolor; }
		
		double radius() const { return _radius; }
		void radius(double sradius) { _radius = sradius; }

		bool selected() const { return _selected; } 
		void selected(bool sel);

		bool tagged() const { return _tagged; }
		void tagged(bool istagged) { _tagged = istagged; }

		// edges
	        
		size_t edge_count() const { return  _edges.size(); }

		EdgeList::iterator e_begin() { return _edges.begin(); }
		EdgeList::iterator e_end() { return _edges.end(); } 

		EdgeList::const_iterator e_begin() const { return _edges.begin(); }
		EdgeList::const_iterator e_end() const { return _edges.end(); } 

		bool has_edge_to(const Vertex *dest) const;

		//bool get_attribute(const std::string& name, std::string& value);
		//void set_attribute(const std::string& name, const std::string& value);
	private:
		Vertex(Universe& u, const Vector& the_pos, const std::string& the_name);

		Universe& _universe;
		Graph *_graph;
		EdgeList _edges;
		std::string _name;
		Vector _position;
		Vector _velocity;
		Vector _force;
		Vector _color;
		double _radius;
		bool _selected;
		bool _tagged;
		//Dictionary<std::string> _attributes;

};

class Edge : private Uncopyable {
	public:
		friend class Universe;

		static Edge* create(Vertex* source, 
				    Vertex* destination, 
				    const Vector& color); 

		Vertex* source() const { return _source; }
		Vertex* destination() const { return _destination; }
		
		const Vector& color() const { return _color; }
		void color(const Vector& color) { _color = color; }

		//bool get_attribute(const std::string& name, std::string& value);
		//void set_attribute(const std::string& name, const std::string& value);			
	private:
		Edge(Vertex* source, 
		     Vertex* destination, 
		     const Vector& scolor);

		Vertex *_source;
		Vertex *_destination;
		Vector _color;
		//Dictionary<std::string> _attributes;
};

class QNode;

class Graph : private Uncopyable {
	public:
		friend class Universe;
		friend class Edge; 
		friend class Vertex; 

		unsigned order() const { return _vertices.size(); }

		VertexList::iterator v_begin() { return _vertices.begin(); }
		VertexList::iterator v_end() { return _vertices.end(); }

		VertexList::const_iterator v_begin() const { return _vertices.begin(); }
		VertexList::const_iterator v_end() const { return _vertices.end(); }

		void apply_force(const Vector& external_force);

		void update_quadtree();
		QNode* quadtree() const { return _qn; }	
		bool intersects(Graph* with_graph) const;

		~Graph(); 
	private:
		void v_add(Vertex *v) 	{ _vertices.push_back(v); v->_graph = this; }

		Graph(Universe& u, Vertex *vinit);
		Graph* merge(Graph *ext);

		Universe& _universe;
		VertexList _vertices;
		EdgeList _edges;
		QNode *_qn;
		float _x1, _y1, _x2, _y2;
		float _d1, _d2, _d3, _d4;
};

class Universe {
	public:
		friend class Vertex;
		friend class Graph;

		Universe(float x1, float y1, float x2, float y2);

		Vertex *get_vertex_by_name(const std::string &name) const { return _vertex_names[name]; }

		Vertex* vertex_locate(const Vector& pos, float tolerance=1.0) const;

		void set_limits(float x1, float y1, float x2, float y2);
		void get_limits(float& x1, float& y1, float& x2, float& y2) const;
		
		float animate(float timestep);

		double energy() const { return _k_energy; }
		
		unsigned size() const { return _vertex_names.size(); }

		Vertex* suspended_vertex() const { return _suspended_vertex; }

                void suspended_vertex(Vertex *target) { _suspended_vertex = target; }
		
		GraphSet::iterator g_begin() { return _graphs.begin(); }
		GraphSet::iterator g_end() { return _graphs.end(); }

		GraphSet::const_iterator g_begin() const { return _graphs.begin(); }
		GraphSet::const_iterator g_end() const { return _graphs.end(); }

		unsigned vertex_selected_count() const { return _selected_count; }

		float kd_parameter() const;
	private:
		DISALLOW_COPY_AND_ASSIGN(Universe);

		float _x1,_y1,_x2,_y2;
	        float _k_energy;
		Vertex *_suspended_vertex; 
		unsigned _selected_count;

		Dictionary<Vertex *> _vertex_names;
		GraphSet _graphs;
};

#endif
